Class {
	#name : 'MCStWriterTest',
	#superclass : 'MCTestCase',
	#instVars : [
		'stream',
		'writer'
	],
	#category : 'Monticello-Tests-IO',
	#package : 'Monticello-Tests',
	#tag : 'IO'
}

{ #category : 'asserting' }
MCStWriterTest >> assertAllChunksAreWellFormed [

	stream := self newChunkReader.
	self assertChunkIsWellFormed: stream next
]

{ #category : 'asserting' }
MCStWriterTest >> assertChunkIsWellFormed: chunk [
	self class compiler
		source: chunk;
		class: UndefinedObject;
		isScripting: true;
		compile
]

{ #category : 'asserting' }
MCStWriterTest >> assertContentsOf: strm match: expected [
	| actual |
	actual := strm contents.
	self assert: actual size equals: expected size.
	actual with: expected do: [ :a :e | self assert: a equals: e ]
]

{ #category : 'asserting' }
MCStWriterTest >> assertMethodChunkIsWellFormed: chunk [
	self class compiler
		source: chunk;
		class: UndefinedObject;
		compile.
	
]

{ #category : 'data' }
MCStWriterTest >> expectedClassDefinitionA [
 ^ '
MCMock subclass: #MCMockClassA
	instanceVariableNames: ''ivar''
	classVariableNames: ''CVar InitializationOrder''
	poolDictionaries: ''''
	category: ''MonticelloMocks''!

!MCMockClassA commentStamp: '''' prior: 0!
This is a mock class. The Monticello tests manipulated it to simulate a developer modifying code in the image.!
'
]

{ #category : 'data' }
MCStWriterTest >> expectedClassDefinitionB [
 ^ '
MCMock subclass: #MCMockClassB
	instanceVariableNames: ''ivarb''
	classVariableNames: ''CVar''
	poolDictionaries: ''MCMockAPoolDictionary''
	category: ''MonticelloMocks''!

MCMockClassB class
	instanceVariableNames: ''ciVar''!

!MCMockClassB commentStamp: '''' prior: 0!
This comment has a bang!! Bang!! Bang!!!
'
]

{ #category : 'data' }
MCStWriterTest >> expectedClassMethodDefinition [

	^ '\s*!MCMockClassA class methodsFor\: ''as yet unclassified'' ' , self stampRegex , '!\s*one\s*\^ 1! !\s*'.
]

{ #category : 'testing' }
MCStWriterTest >> expectedInitializerA [
	^ 'MCMockClassA initialize'
]

{ #category : 'testing' }
MCStWriterTest >> expectedInitializerASubclass [
	^ 'MCMockASubclass initialize'
]

{ #category : 'data' }
MCStWriterTest >> expectedMethodDefinition [
	^ '\s*!MCMockClassA methodsFor\: ''numeric'' ' , self stampRegex , '!\s*one\s*\^ 1! !\s*'
]

{ #category : 'data' }
MCStWriterTest >> expectedMethodDefinitionWithBangs [
	^ '
!MCStWriterTest methodsFor: ''testing'' stamp: ''''!
methodWithBangs
	^ ''
	^ ReadStream on: 
''''MCRevisionInfo packageName: ''''MonticelloCompatibilityTest''''!!!!
MCOrganizationDeclaration categories: 
  #(
  ''''MonticelloMocks'''')!!!!

MCClassDeclaration
  name: #MCMockClassD
  superclassName: #Object
  category: #''''MonticelloMocks''''
  instVarNames: #()
  comment: ''''''''!!!!

MCMethodDeclaration className: #MCMockClassD selector: #one category: #''''as yet unclassified'''' timeStamp: ''''2025-04-25T17:47:11.690218+02:00'''' source: 
''''one
	^ 1''''!!!!
''''
''
! !
'
]

{ #category : 'testing' }
MCStWriterTest >> methodWithBangs [
	^ '
	^ ReadStream on: 
''MCRevisionInfo packageName: ''MonticelloCompatibilityTest''!!
MCOrganizationDeclaration categories: 
  #(
  ''MonticelloMocks'')!!

MCClassDeclaration
  name: #MCMockClassD
  superclassName: #Object
  category: #''MonticelloMocks''
  instVarNames: #()
  comment: ''''!!

MCMethodDeclaration className: #MCMockClassD selector: #one category: #''as yet unclassified'' timeStamp: ''2025-04-25T17:47:11.690218+02:00'' source: 
''one
	^ 1''!!
''
'

]

{ #category : 'testing' }
MCStWriterTest >> newChunkReader [

	^ ChunkReadStream on: stream contents readStream
]

{ #category : 'running' }
MCStWriterTest >> setUp [
	super setUp.
	stream := String new writeStream.
	writer := MCStWriter on: stream.

]

{ #category : 'data' }
MCStWriterTest >> stampRegex [
	"to match stamps like:
	 	stamp: '2025-08-13T13:33:59.50038+00:00'"
	
	^ 'stamp\: ''\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}.\d{5,6}[+-]\d{2}\:\d{2}'''
]

{ #category : 'testing' }
MCStWriterTest >> testClassDefinitionA [

	writer visitClassDefinition: self mockClassA asClassDefinition.
	self assert: stream contents equals: self expectedClassDefinitionA.
	self assertContentsOf: stream match: self expectedClassDefinitionA.
	stream := self newChunkReader.
	2 timesRepeat: [ self assertChunkIsWellFormed: stream next ]
]

{ #category : 'testing' }
MCStWriterTest >> testClassDefinitionB [

	writer visitClassDefinition: self mockClassB asClassDefinition.
	self assert: stream contents equals: self expectedClassDefinitionB.
	self assertContentsOf: stream match: self expectedClassDefinitionB
]

{ #category : 'testing' }
MCStWriterTest >> testClassMethodDefinition [

	writer visitMethodDefinition: (RGMethodDefinition realClass: self mockClassA class selector: #one) asMCMethodDefinition.
	self
		assert: (stream contents matchesRegex: self expectedClassMethodDefinition)
		description: 'Wrong content generated by the MCStWriter. Got: ' , stream contents.

	stream := self newChunkReader.

	self assertEmpty: stream next.
	self assertChunkIsWellFormed: stream next.
	self assertMethodChunkIsWellFormed: stream next.
	self assertEmpty: stream next
]

{ #category : 'testing' }
MCStWriterTest >> testInitializerDefinition [

	| chunk lastChunk penultChunk |
	writer writeSnapshot: self mockSnapshot.
	stream := self newChunkReader.

	[ stream atEnd ] whileFalse: [
			chunk := stream next.
			chunk isAllSeparators ifFalse: [
					penultChunk := lastChunk.
					lastChunk := chunk ] ].
	self assertContentsOf: penultChunk readStream match: self expectedInitializerA.
	self assertContentsOf: lastChunk readStream match: self expectedInitializerASubclass
]

{ #category : 'testing' }
MCStWriterTest >> testMethodDefinition [

	| line |
	writer visitMethodDefinition: (RGMethodDefinition realClass: self mockClassA selector: #one) asMCMethodDefinition.
	self assert: (stream contents matchesRegex: self expectedMethodDefinition).

	stream := self newChunkReader.
	line := stream next.
	self assert: line isAllSeparators description: 'Chunk should start with an empty line but I got: ' , line.
	self assertChunkIsWellFormed: stream next.
	self assertMethodChunkIsWellFormed: stream next.
	line := stream next.
	self assert: line isAllSeparators description: 'Chunk should end with an empty line but I got: ' , line
]

{ #category : 'testing' }
MCStWriterTest >> testMethodDefinitionWithBangs [

	| methodDefinition |
	methodDefinition := (RGMethodDefinition realClass: self class selector: #methodWithBangs) asMCMethodDefinition.
	methodDefinition setTimeStamp: ''.
	writer visitMethodDefinition: methodDefinition.

	self assertContentsOf: stream contents match: self expectedMethodDefinitionWithBangs.

	stream := self newChunkReader.

	self assert: stream next isAllSeparators.
	self assertChunkIsWellFormed: stream next.
	self assertMethodChunkIsWellFormed: stream next.
	self assert: stream next isAllSeparators
]

{ #category : 'testing' }
MCStWriterTest >> testNotLoadedClassMethod [
	"Check that the chunk header is correct for a class method of a class that isn't part of the running system"
	| definition methodStream methodWriter |

	definition := MCMethodDefinition
		className: #NotALoadedClass
		classIsMeta: true
		selector: #reset
		category: 'imagine'
		timeStamp: nil
		source: 'reset

    ^self doReset'.
	methodStream := String new writeStream.
	methodWriter := MCStWriter new
		stream: methodStream;
		yourself.

	methodWriter writeMethodPreamble: definition.
	self assert: methodStream contents trimBoth
			equals: '!NotALoadedClass class methodsFor: ''imagine'' stamp: ''nil''!'.

]

{ #category : 'testing' }
MCStWriterTest >> testOrganizationDefinition [

	| definition systemPackage |
	systemPackage := self mockPackage systemPackage.
	definition := MCOrganizationDefinition packageName: systemPackage name tagNames: systemPackage tagNames.
	writer visitOrganizationDefinition: definition.

	self assertContentsOf: stream match: 'self packageOrganizer ensurePackage: #MonticelloMocks withTags: #()!
'.
	self assertAllChunksAreWellFormed
]

{ #category : 'testing' }
MCStWriterTest >> testOrganizationDefinition2 [

	| definition |
	definition := MCOrganizationDefinition packageName: #'Package-With-Dashes' tagNames: #( #Tag #'Tag-With-Dashes' ).
	writer visitOrganizationDefinition: definition.
	self assertContentsOf: stream match: 'self packageOrganizer ensurePackage: #''Package-With-Dashes'' withTags: #(#Tag #''Tag-With-Dashes'')!
'.
	self assertAllChunksAreWellFormed
]
